 /*******************************************************************************
  * Copyright (c) 2000, 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.core.internal.resources;

 import java.io.DataOutputStream ;
 import java.io.IOException ;
 import java.util.*;
 import org.eclipse.core.internal.watson.IPathRequestor;

 //
 public class MarkerWriter {

     protected MarkerManager manager;

     // version numbers
 public static final int MARKERS_SAVE_VERSION = 3;
     public static final int MARKERS_SNAP_VERSION = 2;

     // type constants
 public static final byte INDEX = 1;
     public static final byte QNAME = 2;

     // marker attribute types
 public static final byte ATTRIBUTE_NULL = 0;
     public static final byte ATTRIBUTE_BOOLEAN = 1;
     public static final byte ATTRIBUTE_INTEGER = 2;
     public static final byte ATTRIBUTE_STRING = 3;

     public MarkerWriter(MarkerManager manager) {
         super();
         this.manager = manager;
     }

     /**
      * Returns an Object array of length 2. The first element is an Integer which is the number
      * of persistent markers found. The second element is an array of boolean values, with a
      * value of true meaning that the marker at that index is to be persisted.
      */
     private Object [] filterMarkers(IMarkerSetElement[] markers) {
         Object [] result = new Object [2];
         boolean[] isPersistent = new boolean[markers.length];
         int count = 0;
         for (int i = 0; i < markers.length; i++) {
             MarkerInfo info = (MarkerInfo) markers[i];
             if (manager.isPersistent(info)) {
                 isPersistent[i] = true;
                 count++;
             }
         }
         result[0] = new Integer (count);
         result[1] = isPersistent;
         return result;
     }

     /**
      * SAVE_FILE -> VERSION_ID RESOURCE+
      * VERSION_ID -> int
      * RESOURCE -> RESOURCE_PATH MARKERS_SIZE MARKER+
      * RESOURCE_PATH -> String
      * MARKERS_SIZE -> int
      * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME
      * MARKER_ID -> long
      * TYPE -> INDEX | QNAME
      * INDEX -> byte int
      * QNAME -> byte String
      * ATTRIBUTES_SIZE -> short
      * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
      * ATTRIBUTE_KEY -> String
      * ATTRIBUTE_VALUE -> INTEGER_VALUE | BOOLEAN_VALUE | STRING_VALUE | NULL_VALUE
      * INTEGER_VALUE -> byte int
      * BOOLEAN_VALUE -> byte boolean
      * STRING_VALUE -> byte String
      * NULL_VALUE -> byte
      * CREATION_TIME -> long
      *
      */
     public void save(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List writtenTypes) throws IOException {
         // phantom resources don't have markers
 if (info.isSet(ICoreConstants.M_PHANTOM))
             return;
         MarkerSet markers = info.getMarkers(false);
         if (markers == null)
             return;
         IMarkerSetElement[] elements = markers.elements();
         // filter out the markers...determine if there are any persistent ones
 Object [] result = filterMarkers(elements);
         int count = ((Integer ) result[0]).intValue();
         if (count == 0)
             return;
         // if this is the first set of markers that we have written, then
 // write the version id for the file.
 if (output.size() == 0)
             output.writeInt(MARKERS_SAVE_VERSION);
         boolean[] isPersistent = (boolean[]) result[1];
         output.writeUTF(requestor.requestPath().toString());
         output.writeInt(count);
         for (int i = 0; i < elements.length; i++)
             if (isPersistent[i])
                 write((MarkerInfo) elements[i], output, writtenTypes);
     }

     /**
      * Snapshot the markers for the specified resource to the given output stream.
      *
      * SNAP_FILE -> [VERSION_ID RESOURCE]*
      * VERSION_ID -> int (used for backwards compatibiliy)
      * RESOURCE -> RESOURCE_PATH MARKER_SIZE MARKER+
      * RESOURCE_PATH -> String
      * MARKER_SIZE -> int
      * MARKER -> MARKER_ID TYPE ATTRIBUTES_SIZE ATTRIBUTE* CREATION_TIME
      * MARKER_ID -> long
      * TYPE -> INDEX | QNAME
      * INDEX -> byte int
      * QNAME -> byte String
      * ATTRIBUTES_SIZE -> short
      * ATTRIBUTE -> ATTRIBUTE_KEY ATTRIBUTE_VALUE
      * ATTRIBUTE_KEY -> String
      * ATTRIBUTE_VALUE -> BOOLEAN_VALUE | INTEGER_VALUE | STRING_VALUE | NULL_VALUE
      * BOOLEAN_VALUE -> byte boolean
      * INTEGER_VALUE -> byte int
      * STRING_VALUE -> byte String
      * NULL_VALUE -> byte
      * CREATION_TIME -> long
      */
     public void snap(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException {
         // phantom resources don't have markers
 if (info.isSet(ICoreConstants.M_PHANTOM))
             return;
         if (!info.isSet(ICoreConstants.M_MARKERS_SNAP_DIRTY))
             return;
         MarkerSet markers = info.getMarkers(false);
         if (markers == null)
             return;
         IMarkerSetElement[] elements = markers.elements();
         // filter out the markers...determine if there are any persistent ones
 Object [] result = filterMarkers(elements);
         int count = ((Integer ) result[0]).intValue();
         // write the version id for the snapshot.
 output.writeInt(MARKERS_SNAP_VERSION);
         boolean[] isPersistent = (boolean[]) result[1];
         output.writeUTF(requestor.requestPath().toString());
         // always write out the count...even if its zero. this will help
 // use pick up marker deletions from our snapshot.
 output.writeInt(count);
         List writtenTypes = new ArrayList();
         for (int i = 0; i < elements.length; i++)
             if (isPersistent[i])
                 write((MarkerInfo) elements[i], output, writtenTypes);
         info.clear(ICoreConstants.M_MARKERS_SNAP_DIRTY);
     }

     /*
      * Write out the given marker attributes to the given output stream.
      */
     private void write(Map attributes, DataOutputStream output) throws IOException {
         output.writeShort(attributes.size());
         for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
             String key = (String ) i.next();
             output.writeUTF(key);
             Object value = attributes.get(key);
             if (value instanceof Integer ) {
                 output.writeByte(ATTRIBUTE_INTEGER);
                 output.writeInt(((Integer ) value).intValue());
                 continue;
             }
             if (value instanceof Boolean ) {
                 output.writeByte(ATTRIBUTE_BOOLEAN);
                 output.writeBoolean(((Boolean ) value).booleanValue());
                 continue;
             }
             if (value instanceof String ) {
                 output.writeByte(ATTRIBUTE_STRING);
                 output.writeUTF((String ) value);
                 continue;
             }
             // otherwise we came across an attribute of an unknown type
 // so just write out null since we don't know how to marshal it.
 output.writeByte(ATTRIBUTE_NULL);
         }
     }

     private void write(MarkerInfo info, DataOutputStream output, List writtenTypes) throws IOException {
         output.writeLong(info.getId());
         // if we have already written the type once, then write an integer
 // constant to represent it instead to remove duplication
 String type = info.getType();
         int index = writtenTypes.indexOf(type);
         if (index == -1) {
             output.writeByte(QNAME);
             output.writeUTF(type);
             writtenTypes.add(type);
         } else {
             output.writeByte(INDEX);
             output.writeInt(index);
         }

         // write out the size of the attribute table and
 // then each attribute.
 if (info.getAttributes(false) == null) {
             output.writeShort(0);
         } else
             write(info.getAttributes(false), output);

         // write out the creation time
 output.writeLong(info.getCreationTime());
     }
 }

